<script setup lang="ts">
import {
  RenderingEngine,
  Types,
  Enums,
  metaData,
  volumeLoader,
  setVolumesForViewports,
  ProgressiveRetrieveImages,
  utilities,
  getEnabledElements, getEnabledElementByIds, setUseCPURendering, imageLoadPoolManager, cache
} from '@cornerstonejs/core'
import _ from 'lodash'
import { initDemo, setCtTransferFunctionForVolumeActor, createImageIdsAndCacheMetaData } from '@/helpers'
import {
  CrosshairsTool, PanTool,
  PlanarRotateTool,
  StackScrollTool,
  Synchronizer,
  SynchronizerManager,
  synchronizers, WindowLevelTool, ZoomTool,
  annotation, StackScrollMouseWheelTool
} from '@cornerstonejs/tools'
import * as cornerstoneTools from '@cornerstonejs/tools'
import { onMounted, provide, reactive, ref, toRef, watch } from 'vue'
import OpButtonGroup from '@/components/OpButtonGroup.vue'
import FourCorner from '@/components/FourCorner.vue'
import { useInitToolGroup } from '@/hooks/useToolGroup'
import { useFourCorner } from '@/hooks/useFourCorner'
import { getCTValue } from '@/hooks/getCTValue'
import { IMG_OP_NAME, IMG_OP_NAME_RESET, IMG_OP_NOT_TOOL_NAME } from '@/types/viewSetting'
import { FOUR_CORNER_SHOW_NAME } from '@/types/types'
import { IEnabledElement } from '@cornerstonejs/core/src/types'

const { ViewportType, RequestType } = Enums
const { imageRetrieveMetadataProvider } = utilities
const {
  Enums: csToolsEnums,
  Types
} = cornerstoneTools
const { MouseBindings } = csToolsEnums
const { getAllAnnotations, removeAnnotation } = annotation.state
// Define a unique id for the volume
// 获取所有支持得色彩空间名称。
// const colormaps = vtkColormaps.rgbPresetNames.map((presetName) =>
//     vtkColormaps.getPresetByName(presetName)
// );


const props = defineProps(['imageIds', 'ids'])

const imageIdsRef = toRef(props, 'imageIds')

const initFlagRef = ref(false)


const volumeId = props.ids.volumeId
const sagRef = ref(null)
const axRef = ref(null)
const corRef = ref(null)
const viewportId1 = props.ids.viewportId1
const viewportId2 = props.ids.viewportId2
const viewportId3 = props.ids.viewportId3

const viewportIds = [viewportId1, viewportId2, viewportId3]
const renderingEngineId = props.ids.renderingEngineId
// Instantiate a rendering engine
let renderingEngine
const { VOI_MODIFIED, VOLUME_NEW_IMAGE } = Enums.Events
const voiSynchronizerId = `VOI_SYNCHRONIZER_${props.ids.renderingEngineId}`
const OpBtns = [IMG_OP_NAME.PAN, IMG_OP_NAME.ZOOM, IMG_OP_NAME.WINDOW, IMG_OP_NAME.SCROLL,
  IMG_OP_NAME.CROSSHAIR, IMG_OP_NAME.LENGTH, IMG_OP_NAME.ANGEL, IMG_OP_NAME.RECT,
  IMG_OP_NAME.FLIP_H, IMG_OP_NAME.FLIP_V, IMG_OP_NAME.INVERT, IMG_OP_NAME.COLOR_MAP,
  IMG_OP_NAME.RESET_CAMERA, IMG_OP_NAME.RESET_PROPERTIES, IMG_OP_NAME.RESET_MEASURE]

const handleNotToolOp = {
  [IMG_OP_NOT_TOOL_NAME.FLIP_H]: () => {
    const viewport = renderingEngine.getViewport(activeViewportIdRef.value)
    const { flipHorizontal } = viewport.getCamera()
    viewport.setCamera({ flipHorizontal: !flipHorizontal })
    viewport.render()
  },
  [IMG_OP_NOT_TOOL_NAME.FLIP_V]: () => {
    const viewport = renderingEngine.getViewport(activeViewportIdRef.value)
    const { flipVertical } = viewport.getCamera()
    viewport.setCamera({ flipVertical: !flipVertical })
    viewport.render()
  },
  [IMG_OP_NOT_TOOL_NAME.INVERT]: () => {
    const viewport: any = renderingEngine.getViewport(activeViewportIdRef.value)
    const { invert } = viewport.getProperties()
    viewport.setProperties({ invert: !invert })
    viewport.render()
  },
  [IMG_OP_NOT_TOOL_NAME.COLOR_MAP]: (colorMapName = 'hsv') => {
    const viewports: any = renderingEngine.getViewports()
    viewports.forEach(viewport => {
      viewport.setProperties({ colormap: { name: colorMapName } })
    })
    renderingEngine.render()
  }
}

const handleReset = {
  [IMG_OP_NAME_RESET.RESET_CAMERA]: () => {
    const viewport: any = renderingEngine.getViewport(activeViewportIdRef.value)
    viewport.resetCamera()
    renderingEngine.render()
  },
  [IMG_OP_NAME.RESET_PROPERTIES]: () => {
    // 注意名称，如果用grayscale会报错。
    // viewport.setProperties({ colormap: { name: 'Grayscale' } }, volumeId);
    const viewports: any = renderingEngine.getViewports()
    viewports.forEach(viewport => {
      viewport.resetToDefaultProperties()
      viewport.setProperties({
        colormap: {
          name: 'Grayscale'
        }
      }, volumeId)
    })
    renderingEngine.render()
  },
  [IMG_OP_NAME.RESET_MEASURE]: () => {
    const viewport = renderingEngine.getViewport(activeViewportIdRef.value)
    const allAnnotations = getAllAnnotations()
    const measureAnns = allAnnotations.filter(annotation => annotation.metadata.toolName !== 'Crosshairs')
    measureAnns.forEach(annotation => {
      removeAnnotation(annotation.annotationUID)
    })
    viewport.render()
  }
}
const { createVOISynchronizer } = synchronizers
let {
  toolGroup,
  activeOp,
  setActive,
  addTools2Render
} = useInitToolGroup(props.ids.toolsGroupId, handleReset, handleNotToolOp)
const { cornerVal, updateCornerVal } = useFourCorner(viewportIds)
provide('toolGroup', toolGroup)
provide('viewportIds', viewportIds)
let volume


const listenEvent = () => {
  for (const viewportId of viewportIds) {
    const enabledElement: IEnabledElement = getEnabledElementByIds(viewportId, renderingEngineId)
    const current = (enabledElement.viewport as any).getSliceIndex()
    const total = enabledElement.viewport.getNumberOfSlices()
    const imagePosition = volume.origin
    const pixelSpacing = volume.spacing
    const posi = getCurrentIPL(viewportId, current, pixelSpacing, imagePosition)

    updateCornerVal(enabledElement.viewportId, {
      [FOUR_CORNER_SHOW_NAME.INDEX]: current,
      [FOUR_CORNER_SHOW_NAME.TOTAL]: total,
      [FOUR_CORNER_SHOW_NAME.View_type]: viewportId,
      [FOUR_CORNER_SHOW_NAME.Position]: posi
    })

    handleVolumeChange(enabledElement)
    handleVOIChange(enabledElement)
    handleMouseMove(enabledElement, viewportId)
  }
}
const handleVolumeChange = (enabledElement) => {
  enabledElement.viewport.element.addEventListener(VOLUME_NEW_IMAGE, ((
    evt: Types.EventTypes.VolumeNewImageEvent
  ) => {
    const { imageIndex, viewportId, numberOfSlices } = evt.detail
    const imagePosition = volume.origin
    const pixelSpacing = volume.spacing
    const posi = getCurrentIPL(viewportId, imageIndex, pixelSpacing, imagePosition)

    updateCornerVal(viewportId, {
      [FOUR_CORNER_SHOW_NAME.INDEX]: imageIndex,
      [FOUR_CORNER_SHOW_NAME.TOTAL]: numberOfSlices,
      [FOUR_CORNER_SHOW_NAME.Position]: posi
    })

  }) as EventListener)
}

const getCurrentIPL = (viewportId, current, pixelSpacing, imagePosition) => {
  let posi
  let posiName = ''
  if (viewportId === viewportId1) {
    posiName = 'I'
    posi = imagePosition[2] + current * pixelSpacing[2]
  } else if (viewportId === viewportId2) {
    posiName = 'R'
    posi = imagePosition[1] + current * pixelSpacing[1]
  } else if (viewportId === viewportId3) {
    posiName = 'P'
    posi = imagePosition[0] + current * pixelSpacing[0]
  }
  posi = `${posiName}: ${Number.parseFloat(posi.toFixed(2))}mm`
  return posi
}

const handleVOIChange = (enabledElement) => {
  // VOI 改变了，更新W的数据。
  enabledElement.viewport.element.addEventListener(VOI_MODIFIED, ((
    evt: Types.EventTypes.VoiModifiedEvent
  ) => {
    const { lower, upper } = evt.detail.range
    updateCornerVal(enabledElement.viewportId, {
      [FOUR_CORNER_SHOW_NAME.WW]: upper - lower,
      [FOUR_CORNER_SHOW_NAME.WL]: (upper + lower) / 2
    })
  }) as EventListener)
}

const handleMouseMove = (enabledElement, viewportId) => {
  enabledElement.viewport.element.addEventListener('mousemove', _.throttle(evt => {

    const rect = enabledElement.viewport.element.getBoundingClientRect()

    const canvasPos = <Types.Point2>[
      Math.floor(evt.clientX - rect.left),
      Math.floor(evt.clientY - rect.top)
    ]

    const worldPos = enabledElement.viewport.canvasToWorld(canvasPos)
    const imagePosition = volume.origin
    const pixelSpacing = volume.spacing
    let rows, columns
    // ax
    if (viewportId === viewportId1) {
      columns = ((worldPos[0] - imagePosition[0]) / pixelSpacing[0]).toFixed(0)
      rows = ((worldPos[1] - imagePosition[1]) / pixelSpacing[1]).toFixed(0)
      // sag
    } else if (viewportId === viewportId2) {
      columns = ((worldPos[1] - imagePosition[1]) / pixelSpacing[1]).toFixed(0)
      rows = ((worldPos[2] - imagePosition[2]) / pixelSpacing[2]).toFixed(0)

    } else if (viewportId === viewportId3) {
      columns = ((worldPos[0] - imagePosition[0]) / pixelSpacing[0]).toFixed(0)
      rows = ((worldPos[2] - imagePosition[2]) / pixelSpacing[2]).toFixed(0)
    }
    const ctValue = getCTValue(volume, worldPos) || -1000
    updateCornerVal(viewportId, {
      [FOUR_CORNER_SHOW_NAME.X]: rows,
      [FOUR_CORNER_SHOW_NAME.Y]: columns,
      [FOUR_CORNER_SHOW_NAME.CT]: ctValue
    })
  }, 50))
}


const setVOISync = () => {
  createVOISynchronizer(voiSynchronizerId)
  const synchronizer =
    SynchronizerManager.getSynchronizer(voiSynchronizerId)
  if (synchronizer instanceof Synchronizer) {
    viewportIds.forEach(viewportId => {
      synchronizer.add({ renderingEngineId, viewportId })
    })
  }
}

async function run() {
  await initDemo()
  renderingEngine = new RenderingEngine(renderingEngineId)
  // 使用CPU 渲染
  // setUseCPURendering(false);
  initFlagRef.value = true
}


const initTools = () => {
  toolGroup.addTool(StackScrollMouseWheelTool.toolName)
  toolGroup.setToolActive(StackScrollMouseWheelTool.toolName)
  viewportIds.forEach(viewportId => {
    addTools2Render(viewportId, renderingEngineId)
  })
}

watch([imageIdsRef, initFlagRef], async ([newIds, newFlag]) => {
  if (newIds && newIds.length > 0 && newFlag) {
    initTools()
    await loadImgs(imageIdsRef.value)
    await initRender()
    listenEvent()
    setVOISync();
    (toolGroup as any).setToolEnabled(CrosshairsTool.toolName, {
      bindings: [{ mouseButton: MouseBindings.Primary }]
    })
  }
})

const loadImgs = async (imageIds) => {
  volume = await volumeLoader.createAndCacheVolume(volumeId, {
    imageIds: imageIds
  })
  if (cornerVal[viewportId1].kV) {
    return
  } else {
    const obj = metaData.get('imgInfoModule', imageIds[0])
    viewportIds.forEach(viewportId => {
      updateCornerVal(viewportId, obj)
    })
  }
}

const initRender = async () => {
  // Create the viewports
  const viewportInputArray: any = [
    {
      viewportId: viewportId1,
      type: ViewportType.ORTHOGRAPHIC,
      element: axRef.value![0],
      defaultOptions: {
        orientation: Enums.OrientationAxis.AXIAL,
        background: <Types.Point3>[0, 0, 0]
      }
    },
    {
      viewportId: viewportId2,
      type: ViewportType.ORTHOGRAPHIC,
      element: sagRef.value![0],
      defaultOptions: {
        orientation: Enums.OrientationAxis.SAGITTAL,
        background: <Types.Point3>[0, 0, 0]
      }
    },
    {
      viewportId: viewportId3,
      type: ViewportType.ORTHOGRAPHIC,
      element: corRef.value![0],
      defaultOptions: {
        orientation: Enums.OrientationAxis.CORONAL,
        background: <Types.Point3>[0, 0, 0]
      }
    }
  ]
  renderingEngine.setViewports(viewportInputArray)

  volume.load()
  await setVolumesForViewports(
    renderingEngine,
    [
      {
        volumeId,
        callback: setCtTransferFunctionForVolumeActor
      }
    ],
    viewportIds
  )
  renderingEngine.renderViewports([viewportId1, viewportId2, viewportId3])
  imageLoadPoolManager.setMaxSimultaneousRequests(RequestType.Interaction, 6)
  imageLoadPoolManager.setMaxSimultaneousRequests(RequestType.Prefetch, 6)
  imageLoadPoolManager.setMaxSimultaneousRequests(RequestType.Thumbnail, 6)
}

onMounted(() => {
  run()
})

const handleClick = viewportId => {
  activeViewportIdRef.value = viewportId
}
const activeViewportIdRef = ref(viewportId1)

const showFourCornerRef = ref(true)

const views = [
  {
    'ref': axRef,
    'class': 'lt',
    'viewportId': viewportId1
  },
  {
    'ref': sagRef,
    'class': 'lb',
    'viewportId': viewportId2
  },
  {
    'ref': corRef,
    'class': 'rt',
    'viewportId': viewportId3
  }
]

</script>
<template>
  <div class="w-full mb-[20px]">
    <OpButtonGroup @setActive="setActive" :active="activeOp" :op-btns="OpBtns" />
  </div>
  <div class="w-full max-w-[900px] ml-auto mr-auto h-[500px] grid grid-cols-2 grid-rows-2 border-2 border-amber-200">
    <template v-for="view in views" :key="view.class">
      <div
        :class="[view.class, activeViewportIdRef === view.viewportId ? ' border-amber-500': 'border-transparent']"
        :ref="view.ref" @mousedown="handleClick(view.viewportId)">
        <four-corner v-if="showFourCornerRef" :four-corner-val="cornerVal[view.viewportId]" />
      </div>
    </template>
  </div>
</template>

<style scoped>
.lt {
  @apply col-span-1 row-span-1 row-start-1 border-4 relative
}

.lb {
  @apply col-span-1 row-span-1 row-start-2 border-4  relative
}

.rt {
  @apply row-span-2 row-start-1 border-4  relative
}
</style>
